﻿using System;
using System.Collections.Generic;
using SoftwareConsulting.BI11.Studio.InternalServices;

namespace SoftwareConsulting.BI11.NumericalMethods.Classification
{    
    /// <summary>
    /// Класс классификации
    /// </summary>
    public class SimpleClassificator : AbstractClassificator
    {        
        private int[] _groupsLengths;//длинны групп признаков (порядок имеет значение!)
        private int _DMinimumGroupMatching;//коэффициент D - минимально необходимое число совпадений по значениям признаков в рамках любой из групп, для того чтобы признать совпадение по всей группе признаков
        private double[] _maximumAttributesDifference; //максимально допустимая разница по каждому из признаков, для того чтобы признать объект, принадлежащим данному классу
        
        /// <summary>
        /// Конструктор класса
        /// </summary>
        public SimpleClassificator():
            base()
        {            
        }

        /// <summary>
        /// Возвращает коэффициент D - минимально необходимое число совпадений по значениям признаков в рамках любой из групп, для того чтобы признать совпадение по всей группе признаков
        /// </summary>
        public int DMinimumGroupMatching
        {
            get
            {
                return _DMinimumGroupMatching;
            }            
        }

        /// <summary>
        /// Устанавливает коэффициент D - минимально необходимое число совпадений по значениям признаков в рамках любой из групп, для того чтобы признать совпадение по всей группе признаков
        /// </summary>
        public void SetDMinimumGroupMatching(int DMinimumGroupMatching_)
        {
            if (DMinimumGroupMatching_ <= 0)
                throw new ArgumentOutOfRangeException("DMinimumGroupMatching_");
            _DMinimumGroupMatching = DMinimumGroupMatching_;
            for (int i = 0; i < _groupsLengths.Length; i++)
            {
                if (_groupsLengths[i] < _DMinimumGroupMatching)
                    throw new BIClassificationAttributesGroupTooShortException(i, _groupsLengths[i], _DMinimumGroupMatching, null);
            }
        }

        /// <summary>
        /// Устанавливает максимально допустимую разницу по каждому из признаков, для того чтобы признать объект, принадлежащим данному классу
        /// </summary>
        /// <param name="maximumAttributesDifference"></param>
        public void SetMaximumAttributesDifference(double[] maximumAttributesDifference)
        {
            if (maximumAttributesDifference.Length != AttributesCount)
                throw new ArgumentException("maximumAttributesDifference");
            _maximumAttributesDifference = maximumAttributesDifference;
        }

        /// <summary>
        /// Устанавливает дополнительную информацию для каждого из признаков
        /// </summary>
        /// <param name="infos"></param>
        public override void SetInfos(double[] infos)
        {
            SetMaximumAttributesDifference(infos);
        }

        /// <summary>
        /// Инициализирует классификатор
        /// </summary>
        /// <param name="classesCount">количество классов</param>
        /// <param name="attributesCount">количество признаков</param>
        /// <param name="groupsLengths">длинны групп признаков</param>
        /// <param name="maximumAttributesDifference">максимально допустимая разница по каждому из признаков, для того чтобы признать объект, принадлежащим данному классу</param>
        /// <param name="DMinimumGroupMatching_">коэффициент D - минимально необходимое число совпадений по значениям признаков в рамках любой из групп, для того чтобы признать совпадение по всей группе признаков</param>
        public void Initialize(int classesCount, int attributesCount, int[] groupsLengths, double[] maximumAttributesDifference, int DMinimumGroupMatching_)
        {
            SetInitialized(false);
            if (classesCount <= 0)
                throw new ArgumentOutOfRangeException("classesCount");
            if (attributesCount <= 0)
                throw new ArgumentOutOfRangeException("attributesCount");
            if (maximumAttributesDifference.Length != attributesCount)
                throw new ArgumentException("maximumAttributesDifference");
            if (groupsLengths.Length <= 0)
                throw new ArgumentOutOfRangeException("groupsLengths");
            _DMinimumGroupMatching = DMinimumGroupMatching_;
            _groupsLengths = groupsLengths;
            for (int i = 0; i < _groupsLengths.Length; i++)
            {
                if (_groupsLengths[i] < _DMinimumGroupMatching)
                    throw new BIClassificationAttributesGroupTooShortException(i, _groupsLengths[i], _DMinimumGroupMatching, null);
            }
            Examples = new List<ClassificationObject>[classesCount];
            RememberedExamples = new List<List<ClassificationObject>[]>();
            AttributesCount = attributesCount;            
            _maximumAttributesDifference = maximumAttributesDifference;            
            LastRecognizedClassesMatching = new int[classesCount];
            for (int i = 0; i < LastRecognizedClassesMatching.Length; i++)
            {
                LastRecognizedClassesMatching[i] = 0;
            }
            SetInitialized(true);
        }
        
        /// <summary>
        /// Классифицирует объект
        /// </summary>
        /// <param name="attributes"></param>
        protected override void OverloadedRecognizeClass(double[] attributes)
        {            
            for (int classIndex = 0; classIndex < Examples.Length; classIndex++)
            {
                List<ClassificationObject> classExamples = Examples[classIndex];
                //запрещено теперь такоеif (classExamples == null) //нет примеров из этого класса
                //запрещено теперь такое    continue;
                for (int exampleIndex = 0; exampleIndex < classExamples.Count; exampleIndex++)
                {
                    ClassificationObject example = classExamples[exampleIndex];
                    int[] groupMatchings = new int[_groupsLengths.Length];
                    for (int groupIndex = 0; groupIndex < _groupsLengths.Length; groupIndex++)
                    {
                        groupMatchings[groupIndex] = 0;
                    }
                    int currentGroupIndex = 0;
                    int allPreviousGroupsLength = _groupsLengths[currentGroupIndex];
                    for (int attributeIndex = 0; attributeIndex < AttributesCount; attributeIndex++)
                    {
                        double attributeValue = example.GetAttributeValueAt(attributeIndex);
                        double recognizingAttributeValue = attributes[attributeIndex];
                        if (attributeIndex >= allPreviousGroupsLength)
                        {
                            currentGroupIndex++;
                            allPreviousGroupsLength += _groupsLengths[currentGroupIndex];
                        }
                        if ((attributeValue != Constants.NullDouble) && (recognizingAttributeValue != Constants.NullDouble))                            
                        {
                            double difference = Math.Abs(attributeValue - recognizingAttributeValue);
                            if (difference >= _maximumAttributesDifference[attributeIndex])
                                continue;
                        }                        
                        groupMatchings[currentGroupIndex] += 1;                        
                    }
                    for (int groupIndex = 0; groupIndex < _groupsLengths.Length; groupIndex++)
                    {
                        if (groupMatchings[groupIndex] >= _DMinimumGroupMatching)
                            LastRecognizedClassesMatching[classIndex] += 1;
                    }
                }
            }
        }
    }
}
